#pragma once

#include "./IonoCorrect/IonoCorrect.hpp"
#include "./TropoCorrect/TropoCorrect.hpp"
#include "./NavCalculate/NavCalculateResult.hpp"
#include "./NavCalculate/NavParameterCalculate.hpp"
#include "./SinglePoint/SinglePointCalculate.hpp"

namespace ns_Calculate
{
    class Calculate
    {
    public:
        static bool GetTropoCorrect(const std::string &inJson, std::string *outJson)
        {
            if (outJson == nullptr)
                return false;

            Json::Reader reader;
            Json::Value inValue;
            if (!reader.parse(inJson, inValue))
                return false;

            std::string rsString = JsonUtil::ValueToString(inValue["SatelliteCoordinates"]);
            std::string rrString = JsonUtil::ValueToString(inValue["ReciverCoordinates"]);
            std::string curTimeString = JsonUtil::ValueToString(inValue["CurTime"]);

            CRDFactory::CRDPtr rs = CRDFactory::MakeCRDPtr(CRDFactory::ValueType::CartesianCRDSys);
            CRDFactory::CRDPtr rr = CRDFactory::MakeCRDPtr(CRDFactory::ValueType::CartesianCRDSys);
            TimeFactory::TimePtr curTime = TimeFactory::MakeTimePtr(TimeFactory::ValueType::GPSTimeSys);

            rs->Decode(rsString);
            rr->Decode(rrString);
            curTime->Decode(curTimeString);

            double ret = TropoCorrect::GetHopfield(rr, rs, curTime);

            Json::Value outValue;
            outValue["TropoCorrect"] = ret;

            Json::FastWriter writer;
            *outJson = writer.write(outValue);

            return true;
        }

        static bool GetIonoCorrect(const std::string &inJson, std::string *outJson)
        {
            if (outJson == nullptr)
                return false;

            Json::Reader reader;
            Json::Value inValue;
            if (!reader.parse(inJson, inValue))
                return false;

            std::string rsString = JsonUtil::ValueToString(inValue["SatelliteCoordinates"]);
            std::string rrString = JsonUtil::ValueToString(inValue["ReciverCoordinates"]);
            std::string curTimeString = JsonUtil::ValueToString(inValue["CurTime"]);
            std::string mesHeaderString = JsonUtil::ValueToString(inValue["GNSSMesHeader"]);
            GNSSType gnssType = static_cast<GNSSType>(inValue["GNSSType"].asInt());

            CRDFactory::CRDPtr rs = CRDFactory::MakeCRDPtr(CRDFactory::ValueType::CartesianCRDSys);
            CRDFactory::CRDPtr rr = CRDFactory::MakeCRDPtr(CRDFactory::ValueType::CartesianCRDSys);
            TimeFactory::TimePtr curTime = TimeFactory::MakeTimePtr(TimeFactory::ValueType::GPSTimeSys);
            GNSSMesHeader::Ptr header = std::make_shared<GNSSMesHeader>();

            rs->Decode(rsString);
            rr->Decode(rrString);
            curTime->Decode(curTimeString);
            header->Decode(mesHeaderString);

            bool ret = IonoCorrect::GetKlobuchar(gnssType, rr, rs, header, curTime);

            Json::Value outValue;
            outValue["IonoCorrect"] = ret;

            Json::FastWriter writer;
            *outJson = writer.write(outValue);

            return true;
        }

        static bool GetPhysics(const std::string &inJson, std::string *outJson)
        {
            if (outJson == nullptr)
                return false;

            Json::Reader reader;
            Json::FastWriter writer;
            Json::Value inValue;
            if (!reader.parse(inJson, inValue))
                return false;

            Json::Value mesRecordValue = inValue["GNSSMesData"];
            Json::Value curTimeValue = inValue["CurTime"];

            std::string mesRecordString = writer.write(mesRecordValue);
            std::string curTimeString = writer.write(curTimeValue);

            TimeFactory::TimePtr curTime = TimeFactory::MakeTimePtr(TimeFactory::ValueType::GPSTimeSys);
            GNSSMesData::Ptr record = std::make_shared<GNSSMesData>();

            curTime->Decode(curTimeString);
            record->Decode(mesRecordString);

            NavCalculateResult cal(record, curTime);
            auto positon = cal.GetPosition();
            auto velocity = cal.GetVelocity();
            auto clockSpeed = cal.GetClockSpeed();
            auto clockBias = cal.GetClockBias();

            Json::Value outValue;
            std::string positionString;
            positon->Encode(&positionString);
            outValue["Position"] = JsonUtil::StringToValue(positionString);
            outValue["Velocity"]["X"] = velocity[0];
            outValue["Velocity"]["Y"] = velocity[1];
            outValue["Velocity"]["Z"] = velocity[2];
            outValue["ClockSpeed"] = clockSpeed;
            outValue["ClockBias"] = clockBias;

            *outJson = writer.write(outValue);

            return true;
        }

        static bool GetSinglePointPositon(const std::string &inJson, std::string *outJson)
        {
            if (outJson == nullptr)
                return false;

            Json::Reader reader;
            Json::FastWriter writer;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
            {
                Log(Debug) << inJson << '\n';
                return false;
            }

            Json::Value obsRecordsValue = inValue["GNSSObsRecords"]["Content"];
            int obsRecordSize = inValue["GNSSObsRecords"]["Size"].asInt();
            Json::Value mesRecordsValue = inValue["GNSSMesRecords"]["Content"];
            int mesRecordSize = inValue["GNSSMesRecords"]["Size"].asInt();

            std::string obsHeaderJson = JsonUtil::ValueToString(inValue["GNSSObsHeader"]);
            std::string mesHeaderJson = JsonUtil::ValueToString(inValue["GNSSMesHeader"]);

            GNSSObsHeader::Ptr obsHeader = std::make_shared<GNSSObsHeader>();
            obsHeader->Decode(obsHeaderJson);
            GNSSMesHeader::Ptr mesHeader = std::make_shared<GNSSMesHeader>();
            mesHeader->Decode(mesHeaderJson);

            std::vector<GNSSObsRecord::Ptr> obsRecords;
            for (int i = 0; i < obsRecordSize; i++)
            {
                std::string obsRecordJson = JsonUtil::ValueToString(obsRecordsValue[i]);
                GNSSObsRecord::Ptr obsRecord = std::make_shared<GNSSObsRecord>();
                obsRecord->Decode(obsRecordJson);
                obsRecords.push_back(obsRecord);
            }

            std::vector<GNSSMesRecord::Ptr> mesRecords;
            for (int i = 0; i < mesRecordSize; i++)
            {
                std::string mesRecordJson = JsonUtil::ValueToString(mesRecordsValue[i]);
                GNSSMesRecord::Ptr mesRecord = std::make_shared<GNSSMesRecord>();
                mesRecord->Decode(mesRecordJson);
                mesRecords.push_back(mesRecord);
            }

            Matrix DOP;
            auto position = CRDFactory::MakeCRDPtr(CRDFactory::ValueType::CartesianCRDSys);
            if (!SinglePointCalculate::Postion(obsRecords, obsHeader, mesRecords, mesHeader, &position, &DOP))
                return false;

            double HDOP = Mathf::Sqrt(DOP[0][0] * DOP[0][0] + DOP[1][1] * DOP[1][1]);
            double VDOP = DOP[2][2];
            double GDOP = Mathf::Sqrt(HDOP * HDOP + VDOP * VDOP + DOP[3][3] * DOP[3][3]);

            Log(Debug) << "定位完毕" << '\n'
                       << '\n';

            std::string positionString;
            position->Encode(&positionString);

            Json::Value positionValue;
            positionValue["Position"] = JsonUtil::StringToValue(positionString);
            positionValue["DOP"]["HDOP"] = HDOP;
            positionValue["DOP"]["VDOP"] = VDOP;
            positionValue["DOP"]["GDOP"] = GDOP;

            *outJson = JsonUtil::ValueToString(positionValue);

            return true;
        }

        static bool GetSinglePointVelocity(const std::string &inJson, std::string *outJson)
        {
            if (outJson == nullptr)
                return false;

            Json::Reader reader;
            Json::FastWriter writer;
            Json::Value inValue;

            if (!reader.parse(inJson, inValue))
            {
                Log(Debug) << inJson << '\n';
                return false;
            }

            Json::Value obsRecordsValue = inValue["GNSSObsRecords"]["Content"];
            int obsRecordSize = inValue["GNSSObsRecords"]["Size"].asInt();
            Json::Value mesRecordsValue = inValue["GNSSMesRecords"]["Content"];
            int mesRecordSize = inValue["GNSSMesRecords"]["Size"].asInt();

            std::string obsHeaderJson = JsonUtil::ValueToString(inValue["GNSSObsHeader"]);
            std::string mesHeaderJson = JsonUtil::ValueToString(inValue["GNSSMesHeader"]);

            GNSSObsHeader::Ptr obsHeader = std::make_shared<GNSSObsHeader>();
            obsHeader->Decode(obsHeaderJson);
            GNSSMesHeader::Ptr mesHeader = std::make_shared<GNSSMesHeader>();
            mesHeader->Decode(mesHeaderJson);

            std::vector<GNSSObsRecord::Ptr> obsRecords;
            for (int i = 0; i < obsRecordSize; i++)
            {
                std::string obsRecordJson = JsonUtil::ValueToString(obsRecordsValue[i]);
                GNSSObsRecord::Ptr obsRecord = std::make_shared<GNSSObsRecord>();
                obsRecord->Decode(obsRecordJson);
                obsRecords.push_back(obsRecord);
            }

            std::vector<GNSSMesRecord::Ptr> mesRecords;
            for (int i = 0; i < mesRecordSize; i++)
            {
                std::string mesRecordJson = JsonUtil::ValueToString(mesRecordsValue[i]);
                GNSSMesRecord::Ptr mesRecord = std::make_shared<GNSSMesRecord>();
                mesRecord->Decode(mesRecordJson);
                mesRecords.push_back(mesRecord);
            }

            Matrix DOP;

            std::vector<double> velocity;
            if (!SinglePointCalculate::Velocity(obsRecords, obsHeader, mesRecords, mesHeader, &velocity, &DOP))
                return false;

            Log(Debug) << "测速完毕" << '\n'
                       << '\n';

            double HDOP = Mathf::Sqrt(DOP[0][0] * DOP[0][0] + DOP[1][1] * DOP[1][1]);
            double VDOP = DOP[2][2];
            double GDOP = Mathf::Sqrt(HDOP * HDOP + VDOP * VDOP + DOP[3][3] * DOP[3][3]);

            Json::Value velocityValue;
            velocityValue["Velocity"][0] = velocity[0];
            velocityValue["Velocity"][1] = velocity[1];
            velocityValue["Velocity"][2] = velocity[2];
            velocityValue["DOP"]["HDOP"] = HDOP;
            velocityValue["DOP"]["VDOP"] = VDOP;
            velocityValue["DOP"]["GDOP"] = GDOP;

            *outJson = JsonUtil::ValueToString(velocityValue);

            return true;
        }
    };
}